package com.zhkc.wisdom.common.base.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;

/**
 * The class Spring context holder.
 */
@Component
@Slf4j
public class SpringContextHolder {

	private static ApplicationContext applicationContext;

	/**
	 * Sets application context.
	 *
	 * @param applicationContext the application context
	 *
	 * @throws BeansException the beans exception
	 */
	public static void setApplicationContext(ApplicationContext applicationContext) {
		SpringContextHolder.applicationContext = applicationContext;
	}

	/**
	 * Gets application context.
	 *
	 * @return the application context
	 */
	public static ApplicationContext getApplicationContext() {
		assertApplicationContext();
		return applicationContext;
	}

	/**
	 * 主动向Spring容器中注册bean
	 *
	 * @param applicationContext Spring容器
	 * @param name               BeanName
	 * @param clazz              注册的bean的类性
	 * @param args               构造方法的必要参数，顺序和类型要求和clazz中定义的一致
	 * @param <T>
	 * @return 返回注册到容器中的bean对象
	 */
	public static <T> T registerBean(ConfigurableApplicationContext applicationContext, String name, Class<T> clazz,
			Object... args) {
		if (applicationContext.containsBean(name)) {
			Object bean = applicationContext.getBean(name);
			if (bean.getClass().isAssignableFrom(clazz)) {
				return (T) bean;
			} else {
				throw new RuntimeException("BeanName 重复 " + name);
			}
		}
		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
		for (Object arg : args) {
			beanDefinitionBuilder.addConstructorArgValue(arg);
		}
		BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
		BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry) applicationContext.getBeanFactory();
		beanFactory.registerBeanDefinition(name, beanDefinition);
		return applicationContext.getBean(name, clazz);
	}

	/**
	 * 主动向Spring容器中注册bean
	 *
	 * @param applicationContext Spring容器
	 * @param name               BeanName
	 * @param clazz              注册的bean的类性
	 * @param propertys          属性Key
	 * @param args               属性Value
	 * @param <T>
	 * @return 返回注册到容器中的bean对象
	 */
	public static <T> T registerBean(ConfigurableApplicationContext applicationContext, String name, Class<T> clazz,
			String propertys[], Object[] args) {
		if (applicationContext.containsBean(name)) {
			Object bean = applicationContext.getBean(name);
			if (bean.getClass().isAssignableFrom(clazz)) {
				return (T) bean;
			} else {
				throw new RuntimeException("BeanName 重复 " + name);
			}
		}
		if (propertys == null || args == null || args.length != propertys.length) {
			throw new IllegalArgumentException();
		}
//		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(clazz);
		for (int i = 0; i < propertys.length; i++) {
			beanDefinitionBuilder.addPropertyValue(propertys[i], args[i]);
		}
		BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
		BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry) applicationContext.getBeanFactory();
		beanFactory.registerBeanDefinition(name, beanDefinition);
		return applicationContext.getBean(name, clazz);
	}

	/**
	 * Gets bean.
	 *
	 * @param <T>      the type parameter
	 * @param beanName the bean name
	 *
	 * @return the bean
	 */
	public static <T> T getBean(String beanName) {
		assertApplicationContext();
		try {
			return (T) applicationContext.getBean(beanName);
		} catch (Exception e) {
			log.error("取出bean异常name=" + beanName, e.getMessage());
		}
		return null;
	}

	/**
	 * <p>
	 * 通过名称和class获取指定bean
	 * </p>
	 * 
	 * @param name
	 * @param clazz
	 * @return
	 */
	public static <T> T getBean(String name, Class<T> clazz) {
		T t = null;
		try {
			t = getApplicationContext().getBean(name, clazz);
		} catch (Exception e) {
			log.error("取出bean异常name=" + clazz.getName(), e.getMessage());
		}
		return t;
	}

	/**
	 * Gets bean.
	 *
	 * @param <T>          the type parameter
	 * @param requiredType the required type
	 *
	 * @return the bean
	 */
	public static <T> T getBean(Class<T> requiredType) {
		assertApplicationContext();
		try {
			return applicationContext.getBean(requiredType);
		} catch (Exception e) {
			log.error("取出bean异常class=" + requiredType.getSimpleName(), e.getMessage());
		}
		return null;
	}

	public static DefaultListableBeanFactory getDefaultListableBeanFactory() {
		assertApplicationContext();
		return (DefaultListableBeanFactory) ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
	}

	private static void assertApplicationContext() {
		if (SpringContextHolder.applicationContext == null) {
			throw new IllegalArgumentException("applicationContext属性为null,请检查是否注入了SpringContextHolder!");
		}
	}
}
